package com.bot.core;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.jibble.pircbot.*;

public class LostBot extends PircBot{
	private String countdownChannel = "#TheSwan";
	private TimerBot timerNumbers;
	private SqlUsers sql;
	private boolean authmode = false;
	private long lastkey = 0;
	private final static String owner = "EtherNet";
	private final static  String passwd = "3dqzx1";

    /**
	 * @return the lastkey
	 */
	public long getLastkey() {
		return lastkey;
	}

	/**
	 * @param lastkey the lastkey to set
	 */
	public void setLastkey(long lastkey) {
		this.lastkey = lastkey;
		System.out.println("FAILSAFE KEY: " + this.lastkey);
	}

	public String getCountdownChannel() {
		return countdownChannel;
	}

	public LostBot() throws Exception {
        this.setName("TheSwan");
        this.setLogin("TheSwan");
        this.setAutoNickChange(true);
    	this.sql = new SqlUsers();
    	this.setVersion("LostBoT v0.1 - Written by Gerardo Canosa and Javier Joaquin.");
    }

	public void startTimer() {
		this.timerNumbers  = new TimerBot(this);
		timerNumbers.run();							// Init counting.
	}
	public void onConnect() {
        // Services identify
		this.identify("3dqzx1");
        // Join the #TheSwan channel.
        this.joinCountdownChannel("#TheSwan");
	}
	
		// reconnect on disconnect
	public void onDisconnect() {
		try {
			this.reconnect();
		} catch (NickAlreadyInUseException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (IrcException e) {
			e.printStackTrace();
		}
	}
    public void onMessage(String channel, String sender, String login, String hostname, String message) {
    	String[] msgParameters = message.trim().split("\\s+", 2);
        // check if the lost numbers are entered.
    	if (message.equals("4 8 15 16 23 42")) {
            try {
				timerNumbers.pushButton(sender);
			} catch (NotYetException e) {
				// button cannot be pushed yet
			} catch (SQLException e) {
				e.printStackTrace();
			}
        }
    	// if the FailSafe key is entered within 0 secs and -360 secs.
    	else if (message.equals(String.valueOf(this.getLastkey())) &&  timerNumbers.canFailSafeKeyBeEntered()){
    		try {
				timerNumbers.enterFailSafeKey(sender);
			} catch (NotYetException e) {
				System.out.println("Error when enter failsafe key: " + e.getMessage());
			} catch (SQLException e) {
				e.printStackTrace();
			}
    		this.blankLastKey();
    	}
    	// Reset channel modes.
    	else if (msgParameters[0].equalsIgnoreCase(".resetmodes") && this.isOwnerAuth(sender)) {
    		this.resetChannel(channel);
    	}
    	// Hall of Fame
    	else if (msgParameters[0].equalsIgnoreCase(".hof")) {

    	}
    	// show user's pushes
    	else if (msgParameters[0].equalsIgnoreCase(".me")) {
			try {
				//System.out.println(sql.getAmountOfPushesPerNick(sender));
				this.sendMessage(channel, sender + " has pushed the button: " + String.valueOf(sql.getAmountOfPushesPerNick(sender)) + " time" + (Integer.valueOf(sql.getAmountOfPushesPerNick(sender)) <= 1 ? "" : "s") + "." );
			} catch (Exception e) {
				e.printStackTrace();
			}
    	}

    	// Show pushes for an specific user
    	else if (msgParameters[0].equalsIgnoreCase(".nick") || msgParameters[0].equalsIgnoreCase(".user")) {
    		String[] nickparams = msgParameters[1].split("\\s+", 2);

    		try {
				System.out.println(sql.getAmountOfPushesPerNick(nickparams[0]));
				this.sendMessage(channel, nickparams[0] + " has pushed the button: " + sql.getAmountOfPushesPerNick(nickparams[0]) + " time" + (Integer.valueOf(sql.getAmountOfPushesPerNick(nickparams[0])) <= 1 ? "" : "s") + "." );
			} catch (Exception e) {
				e.printStackTrace();
			}
    	}

    	// shows time reamining.
    	else if (message.equalsIgnoreCase(".time")) {
    		this.sendMessage(channel, timerNumbers.getCurrentTimeString());
    	}

    	else if (message.startsWith(".info")) {
    		try {
    			float failratio = (Float.valueOf(this.getAmountOfFailures())/Float.valueOf(this.getTotalRuns())*100);
				this.sendMessage(channel, "Amount of pushes: " + this.getAmountOfPushes() + " - Amount of failures: " + this.getAmountOfFailures() + " - Amount of FailsafeKey entries: " + this.getAmountOfFailSafeEntries() + " - Fail ratio: " + String.valueOf(failratio));
			} catch (SQLException e) {
				e.printStackTrace();
			}
    	}

    	else if (message.startsWith(".ratio")) {
    		try {
    			float ratio = (Float.valueOf(sql.getAmountOfPushesPerNick(sender)) / Float.valueOf(this.getAmountOfPushes()))*100;
				this.sendMessage(channel, "Ratio: " + String.valueOf(ratio));
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (Throwable e) {
				e.printStackTrace();
			}
    	}

    	else if (message.equalsIgnoreCase(".quit") && isOwnerAuth(sender)) {
    		this.resetChannel(channel);
    		this.quitServer("LostBoT v0.1 - SYSTEM FAILURE");
    		System.out.println("Disconnected from " + this.getServer());
    		System.out.println("Exiting.");
    		System.exit(0);
    	}
    	
    	else if (message.equalsIgnoreCase(".reset") && isOwnerAuth(sender)) {
    		this.resetChannel(channel);
    		timerNumbers.setSecs(6480);
    		System.exit(0);
    	}
    }

	private void resetChannel(String channel) {
    	this.setMode(channel, "-im");
	}

	public void onPrivateMessage(String sender, String login, String hostname, String message) {
    	String[] msgParameters = message.trim().split("\\s+", 2);

    	if (msgParameters[0].equalsIgnoreCase(".auth")) {
	    	String[] authParams = msgParameters[1].split("\\s+", 2);
	    	boolean userok = false;
	    	boolean passwdok = false;

			if ( authmode == false) {
				if (authParams[0].equalsIgnoreCase(owner))
		    		userok = true;
		    	else
		    		this.sendNotice(sender, "Invalid username");
				if (authParams[1].equalsIgnoreCase(passwd))
		    		passwdok = true;
		    	else
		    		this.sendNotice(sender, "Invalid password");

		    	if ( userok == true && passwdok == true) {
		    		this.sendNotice(owner, "You has been authenticated as owner");
		    		this.sendMessage(this.getCountdownChannel(), owner + " - Has been authenticated as owner");
		    		System.out.println(owner + " has been authenticated as owner.");
		    		authmode = true;
		    	}
	    	} else {
	    		this.sendNotice(sender, sender + ": Sorry, you has been already authenticated.");
	    	}
      	}
    }

    /**
     * Returns true or false if the owner is authenticated and the one who is asking is the owner.
     * @param sender
     * @return
     */
    public boolean isOwnerAuth(String sender) {
    	if (this.authmode==true && sender.equalsIgnoreCase(owner) )
    		return true;
    	else
    		return false;
    }

    /**
     * Joins to the channel where the game will occur.
     * @param channel
     */
	public void joinCountdownChannel(String channel) {
		this.countdownChannel = channel;
		this.joinChannel(channel);
		this.setTopicChannelOnJoin(channel);
		this.startTimer();
	}

	/**
	 * Add a new push into the DB for a specific user.
	 * @param sender
	 * @param date
	 */
	public void addUserPush(String sender, String date) {
		try {
			sql.insertPushToUser(sender, date, timerNumbers.getSecs());
		} catch (SQLException e) {
			System.out.println("Failed to insert data into db");
			e.printStackTrace();
		}
	}

	/**
	 * Add the key into the DB so that the failsafe's website can offer the key to be inserted on the channel in order to avoid the explotion.
	 * @param date
	 * @throws SQLException
	 */
	public void createFailsafeKeyAndUpdateDB(String date) throws SQLException {
		Statement stat = sql.conn.createStatement();
		this.setLastkey(this.randFailsafeKeyMaker(999999999,167000670));
		//stat.executeUpdate("INSERT INTO web_data VALUES (\"" + this.getLastkey() + "\", \"" + date + "\")");
		stat.executeUpdate("UPDATE web_data SET key='" + this.getLastkey() + "' WHERE rowid=1");
		stat.executeUpdate("UPDATE web_data SET date='" + date + "' WHERE rowid=1");
	}
	private void setTopicChannelOnJoin(String channel) {
		this.setTopic(channel, "|[108|00]| - Time is ticking, enter the Numbers to save the world - Triggers are: .time / .info / .ratio / .hof / .user <nick>");
	}
	
	/**
	 * Blank LastKey
	 */
	public void blankLastKey() {
		this.setLastkey(0);
	}

	/**
	 * Generates random numbers between a MAX value and MIN.
	 * @param max
	 * @param min
	 * @return
	 */
	public long randFailsafeKeyMaker(long max, long min) {
		return (long) ((Math.random()*(max-min))+min);
	}
	
	/**
	 * Add a failure to DB
	 * @throws SQLException
	 */
	public void addFailureToDB() throws SQLException {
		 Statement stat = sql.conn.createStatement();
		 stat.executeUpdate("UPDATE stats SET failures = (SELECT failures FROM stats) +1");
	}
	
	public void addPushToDB() throws SQLException {
		Statement stat = sql.conn.createStatement();
		stat.executeUpdate("UPDATE stats SET pushes = (SELECT pushes FROM stats) +1");
	}
	
	public void addFailSafeToDB() throws SQLException {
		Statement stat = sql.conn.createStatement();
		stat.executeUpdate("UPDATE stats SET failsafeuses = (SELECT failsafeuses FROM stats) +1");
	}
	
	public int getAmountOfPushes() throws SQLException {
		Statement stat = sql.conn.createStatement();
	    ResultSet resultSet = stat.executeQuery("SELECT COUNT(1) from user_pushes");
	    resultSet.next();
	    
	    int sum = resultSet.getInt(1); 
	    
	    resultSet.close();
	    
	    return sum;
	}
	
	public int getAmountOfFailures() throws SQLException {
		Statement stat = sql.conn.createStatement();
	    ResultSet resultSet = stat.executeQuery("SELECT failures FROM stats");
	    resultSet.next();
	    
	    int sum = resultSet.getInt(1); 
	    
	    resultSet.close();
	    
	    return sum;
	}
	
	public int getAmountOfFailSafeEntries() throws SQLException {
		Statement stat = sql.conn.createStatement();
	    ResultSet resultSet = stat.executeQuery("SELECT failsafeuses FROM stats");
	    resultSet.next();
	    
	    int sum = resultSet.getInt(1); 
	    
	    resultSet.close();
	    
	    return sum;
	}
	
	public int getTotalRuns() throws SQLException {
		return this.getAmountOfPushes() + this.getAmountOfFailures() + this.getAmountOfFailSafeEntries();
	}
}
